from twisted.internet.protocol import DatagramProtocol
from string import split,join,atoi
from array import array
from struct import unpack
from time import time
import pygtk
pygtk.require('2.0')
import gobject
def sanitize(s):
	color="black"
	colors_inv={'0':'white', '1':'red', '2':'#008800', '3':'#888800', '4':'blue', '5':'Cyan', '6':'ping', '7':'black', '8':'white', '9':'red'}
	colors={'0':'#505050', '1':'#AA0000', '2':'#00AA00', '3':'#AAAA00', '4':'#0000AA', '5':'#00AAAA', '6':'#AA00AA', '7':'black', '8':'#888888', '9':'red'}

	#s=s.replace("|", "\\|")
	s2=""
	for c in s:
		if(ord(c)<32):
			s2+='#'
		else:
			s2+=c
	s=s2
	s=s.replace("&", "&amp;")
	s=s.replace("<", "&lt;")
	s=s.replace(">", "&gt;")

	s="^7"+s
	s=split(s,"^")
	o=""
	for w in s:
			o+=w[1:]

	return o	

def sanitize_to_html(s):
	color="black"
	colors_inv={'0':'white', '1':'red', '2':'#008800', '3':'#888800', '4':'blue', '5':'Cyan', '6':'ping', '7':'black', '8':'white', '9':'red'}
	colors={'0':'#505050', '1':'#AA0000', '2':'#00AA00', '3':'#AAAA00', '4':'#0000AA', '5':'#00AAAA', '6':'#AA00AA', '7':'black', '8':'#888888', '9':'red'}

	#s=s.replace("|", "\\|")
	s2=""
	for c in s:
		if(ord(c)<32):
			s2+='#'
		else:
			s2+=c
	s=s2
	s=s.replace("&", "&amp;")
	s=s.replace("<", "&lt;")
	s=s.replace(">", "&gt;")

	s="^7"+s
	s=split(s,"^")
	o=""
	for w in s:
		if(len(w)>1 and colors.has_key(w[0])):
			o+="<span foreground=\"%s\">%s</span>" % (colors[w[0]], w[1:])
		else:
			o+=w[1:]

	return o	
	
class Servers:
	refreshinterval=3
	timer_callback=None
	max_history=3*60/refreshinterval # 3 minutes of history
	i=0
	history={}
	callbacks={}
	def __init__(self):
		self.reset()
		self.timer()
	def reset(self,):
		self.servers={}
	def doHistory(self, addr, when, ping):
		if not self.history.has_key(addr):
			self.history[addr]=[]
		self.history[addr].append([addr, when, ping])
		if len(self.history[addr])>self.max_history:
			try:
				self.history[addr]=self.history[addr][len(self.history[addr])-self.max_history:]
			except :
				print "ERROR doHistory(\"%s\", %s, %s)" % (addr, when, ping)

	def doSent(self,s, create=True):
		if not self.servers.has_key(s):
			if create:
				self.servers[s]=[time(),0, None]
			else:
				self.servers[s]=[None,0,None]
		else:
			self.servers[s][0]=time()
	def doReceived(self,s):
		if self.servers.has_key(s):
			p=(time()-self.servers[s][0])*1000
			if p>999:
				p=999
			self.doHistory(s, self.servers[s][0], p)
			self.servers[s][1]=p
			self.servers[s][0]=0

			return p
		else:
			return None
	def timer(self):
		if len(self.servers)>0:
			t=self.refreshinterval*1000/len(self.servers)	
			self.timer_id = gobject.timeout_add(t, self.run)
			self.noservers=False
		else:
			self.timer_id = gobject.timeout_add(3000, self.run)
			self.noservers=True
	def run(self):
		if len(self.servers)==0:
			self.timer()
			return False
		if self.i>=len(self.servers):
			self.i=0
			self.timer()
			return False
		
		addr=list(self.servers.keys())[self.i]
		
		if not	self.get_time(addr):
			self.doSent(addr)
		(host,port)=split(addr, ':')
		
		if self.timer_callback!=None:
			self.timer_callback(host, atoi(port))
		self.i+=1
		if self.i==1 and self.noservers:
			self.timer()
			return False
		return True

	def set_listiter(self, s,v):
		if self.servers.has_key(s):
			self.servers[s][2]=v
		else:
			pass
		return False
			
	def get_listiter(self, s):
		if self.servers.has_key(s):
			return self.servers[s][2]
		else:
			return None	
	def get_time(self,s):
		if self.servers.has_key(s):
			return self.servers[s][0]
		else:
			return None
	def get_ping(self,s):
		if self.servers.has_key(s):
			return self.servers[s][1]
		else:
			return None
		
class GameProtocol(DatagramProtocol):
	callbacks={}
	def addCallback(self,addr, func):
		self.callbacks[addr]=func
	
	def removeCallback(self,addr):
		if self.callbacks.has_key(addr):
			del self.callbacks[addr]

	def getCallback(self,addr):
		if self.callbacks.has_key(addr):
			return self.callbacks[addr]
		else:
			return None
class TremulousProtocol(GameProtocol):
	def startProtocol(self):
		self.start()
	def start(self):
		self.servers=Servers()
		self.servers.timer_callback=self.getstatus
		
	def get_server_list(self):
		self.servers=Servers()
		self.servers.timer_callback=self.getstatus
		reactor.resolve('tremmaster.tjw.org').addCallback(self.get_server_list_cb)

	def get_server_list_cb(self, ip):
		print "get_server_list"
		self.servers.reset()
		self.transport.write("\xFF\xFF\xFF\xFFgetservers 69 full empty", (ip, 30710)) ;


	def getstatus(self, ip, port):
		self.transport.write("\xFF\xFF\xFF\xFFgetstatus", (ip,port))

	
	def getinforesponse(self, host, port, msg, p):
		plist=""
		info={}
		parts=split(msg, "\n")
		players=[]	
		addr="%s:%i" % (host, port)
		
		for part in parts[1:]:
		
		#parse variables block
			if not len(part):
				continue
			if part[0]=="\\":
				ll=split(part[1:], "\\");
				for i in range((len(ll))/2):
					info[ll[i*2]]=ll[(i*2)+1]
			else:
				pp=split(part, " ")
				
				players.append([pp[0], pp[1], sanitize(join(pp[2:], " ")[1:-1]), 'Humano'])
		clients=len(players)
		if info.has_key('P'):
			#j=split(info['P'], "\n")
			#print "%s:" % j[0]
		
			teams=info['P'].replace("\n", '').replace('-', '')

			info['clients']="%i" % clients
			plist=""
			if clients and len(teams):

				team_names={'0':"Espectador", '1':"Alien", '2':"Humano"}

				for i in range(clients):
					players[i][3]=team_names[teams[i]]
					plist+="%s %s\n" % (team_names[teams[i]], players[i])
				
			else:
				plist="%s" % players
		if (not info.has_key('clients')):
			info['clients']="%s" % clients
		if (not info.has_key('pure')):
			info['pure']=0

		
		if info.has_key('game'):
			tmapa="<b>%s</b>/%s" % (info['game'], info['mapname'])
		elif info.has_key('gamename'):
			info['game']=info['gamename']
			tmapa="<b>%s</b>/%s" % (info['gamename'], info['mapname'])
		else:
			tmapa=info['mapname']

		if (not info.has_key('maxPing')):
			info['maxPing']='999';
			tping="%i" % int(p)
		else:
			tping="%i (%s)" % (p, info['maxPing'])

		if (not info.has_key('hostname')):
			if info.has_key('sv_hostname'):
				info['hostname']=info['sv_hostname']
			else:
				info['hostname']=''
		misc=[]
		for k in info.keys():
			if not k in ('hostname', 'clients', 'sv_maxclients', 'mapname', 'game', 'pure', 'maxPing'):
				misc.append("%s:%s" % (k,info[k]))
		misc=' , '.join(misc)
		if not clients and not plist:
			plist=misc

		'''row=["%s" % (sanitize(info['hostname'])), 
			atoi(info['clients']),
			'%s (%s)' % (info['clients'],
			info['sv_maxclients']), 
			info['mapname'],
			info['game'], 
			plist,
			False,
			atoi(info['maxPing']),
			info['maxPing'],
			p,
			tping,
			atoi(info['clients'])*100/atoi(info['sv_maxclients']),
			info,
			players,
			addr, icon,get_flag_from_addr(host)]
		'''
		assert(self.servers.servers.has_key(addr))
		
		
		cb=self.getCallback(addr)
		if not cb is None:
			cb(addr, info, players)
				
		

	def getserverResponse(self, data):
		l=split(data, "\\");
		for s in l[1:]:
			if len(s)<6:
				continue
			ip=[]
			for i in s[:4]:
				ip.append(str(ord(i)))
			ip=join(ip, '.')
			port=ord(s[4])*256 + ord(s[5])
			if port==0:
				continue
			self.servers.doSent("%s:%s" % (ip, port), False)
			addr="%s:%i" % (ip,port)
	
			#self.getstatus(ip, port)
			#print "%s:%s\n" % (ip,port)
			
	def datagramReceived(self, data, (host,port)):
		
		if(data.find("\xFF\xFF\xFF\xFFgetserversResponse")==0):
			print "Recibida lista de servidores"
			self.ping={}
			self.getserverResponse(data)
		elif(data.find("\xFF\xFF\xFF\xFFinfoResponse")==0 or
			data.find("\xFF\xFF\xFF\xFFstatusResponse")==0):

			p=self.servers.doReceived("%s:%s" % (host,port))

			self.getinforesponse(host, port, data, p)
		else:
			print "DESCARTADO: received %r from %s:%d" % (data, host, port)

